package simulatore;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import random.Random;

public class Simulatore_MM1FCFS extends SimulatoreStazioneSingola {
	
	protected List<Event> eventList;
	protected List<Event> availList;
	
	protected Random random;
	protected double lambda;
	protected double mu;
	
	// Variabili necessarie per calcolare media e varianza della sorgente (solo in caso di debug)
	protected double lambdaSum;
	protected double lambdaSum2;
	protected int lambdaNum;
	protected double muSum;
	protected double muSum2;
	protected int muNum;
	
	
	/*
	 * 	protected List<Event> eventList;
	 *	protected List<Event> availList;
	 * 
	 */
	
	public Simulatore_MM1FCFS(double lambda, double mu, double start_time, String seed, String mode) {
		
		//super(start_time, "SINGLERUN");
		//this.initializeSingleRun(end_time);
		
		//super(start_time, end_time);
		
		super(start_time, mode);
		//this.initializeRigenerativo(0.05,30,10);
		//this.initializeRigenerativo(0.5,2,2);
				
		this.lambda = lambda;
		this.mu = mu;
		
		if(debug > 1) {
			lambdaSum = 0.0;
			lambdaSum2 = 0.0;
			lambdaNum = 0;
			muSum = 0.0;
			muSum2 = 0.0;
			muNum = 0;
		}
		
		eventList = new LinkedList<Event>();
		availList = new LinkedList<Event>();
		
		random = new Random();
		random.initialize(seed);		
		
	}
	
	public Event ottieniPrimoEventoSchedulato() {
		Event e = eventList.remove(0);
		return e;		
	}
	
	public void aggiuntiEventoAvailList(Event e) {
		availList.add(e);
		
	}
	
	public double ottieniIntervalloTraArriviSuccessivi() {
		
		double temp = random.nextNegExp(lambda);
		if(debug > 1) {
			lambdaSum = lambdaSum + temp;
			lambdaSum2 = lambdaSum2 + (temp * temp);
			lambdaNum++;
		}
		if(debug > 2)  System.out.println("Intervallo tra arrivi: " + temp);
		
		return temp;
		
	}
	public double ottieniTempoDiServizio() {
		
		double temp = random.nextNegExp(mu);
		if(debug > 1) {
			muSum = muSum + temp;
			muSum2 = muSum2 + (temp * temp);
			muNum++;
		}		
		if(debug > 2)  System.out.println("Tempo di servizio:" + temp);
		
		return temp;
	}
	
	// Disciplina di coda: First Come, First Served
	public void schedula(String type, double time, double arrTime) {
		
		Event e;
		
		if(availList.isEmpty()) {
			e = new Event(type, arrTime);
		} else {
			e = availList.remove(0);
			e.setEventType(type);
			e.setOccurTime(arrTime);
		}
		
		Iterator<Event> it = eventList.iterator();
		Event temp;
		int i = 0;
		boolean found = false;
		while(it.hasNext() && !found) {
			temp = it.next();
			if(e.getOccurTime() < temp.getOccurTime()) {
				eventList.add(i,e);
				found = true;
			}
			i++;			
		}
		if(!found)		eventList.add(e);
	}

	public void Report() {
		double ro = lambda / mu;
		double nbar = ro / (1.0 - ro);
		
		super.Report();
		
		//System.out.println("Numero atteso di presenze: " + nbar);
		//System.out.println();
		
		if(debug > 1) {
			double e_lambda = lambdaSum / lambdaNum;
			double e2_lambda = lambdaSum2 / lambdaNum;
			double var_lambda = e2_lambda - (e_lambda * e_lambda);
			double e_mu = muSum / muNum;
			double e2_mu = muSum2 / muNum;
			double var_mu = e2_mu - (e_mu * e_mu);
			System.out.println("Media dei tempi di arrivo: " + e_lambda);
			System.out.println("Media algebrica dei tempi di arrivo: " + (1/lambda));
			System.out.println("Varianza dei tempi di arrivo: " + var_lambda);
			System.out.println("Varianza algebrica dei tempi di arrivo: " + (1/Math.pow(lambda,2)));
			System.out.println("Media dei tempi di servizio: " + e_mu);
			System.out.println("Media algebrica dei tempi di servizio: " + (1/mu));
			System.out.println("Varianza dei tempi di servizio: " + var_mu);
			System.out.println("Varianza algebrica dei tempi di servizio: " + (1 / Math.pow(mu,2)));
			System.out.println();
		}
		
		
	}
	
	@Override
	public void PrintStatus() {
		
		super.PrintStatus();
		
		System.out.println("Stato delle liste del sistema:");
		System.out.println();
		System.out.println("eventList:");
		Iterator<Event> it = eventList.iterator();
		Event e;
		int i = 1;
		while(it.hasNext()) {
			e = it.next();
			System.out.println("Evento " + i + ":");
			//System.out.println("Event class:" + e.getEventClass());
			//System.out.println("Arrival Time:" + e.getArrivalTime());
			//System.out.println("Service Time:" + e.getServiceTime());
			System.out.println("Event Number:" + e.getObjectNumber());
			System.out.println("Event Type:" + e.getEventType());
			System.out.println("Occur Time:" + e.getOccurTime());
			System.out.println();
			i++;
		}	
		System.out.println();
		System.out.println("availList:");
		it = availList.iterator();
		i = 1;
		while(it.hasNext()) {
			e = it.next();
			System.out.println("Evento " + i + ":");
			//System.out.println("Event class:" + e.getEventClass());
			//System.out.println("Arrival Time:" + e.getArrivalTime());
			//System.out.println("Service Time:" + e.getServiceTime());
			System.out.println("Event Number:" + e.getObjectNumber());
			System.out.println("Event Type:" + e.getEventType());
			System.out.println("Occur Time:" + e.getOccurTime());
			System.out.println();
			i++;			
		}	
		System.out.println("#############################################################");
		System.out.println();
		
	}
	
	
	
}
